package com.wlyuan.index.domain.index.service;

import com.alibaba.fastjson2.JSON;
import com.wlyuan.core.exception.ValueError;
import com.wlyuan.index.api.dto.waybill.WaybillIndexFilterDTO;
import com.wlyuan.index.api.dto.waybill.WaybillIndexSearchDTO;
import com.wlyuan.index.api.vo.WaybillIndexSumVO;
import com.wlyuan.index.api.vo.WaybillIndexVO;
import com.wlyuan.index.domain.index.WaybillIndex;
import com.wlyuan.index.domain.index.WaybillIndexScroll;
import com.wlyuan.index.domain.index.builder.WaybillIndexSumBuilder;
import com.wlyuan.index.infrastructure.config.WaybillIndexProperties;
import com.wlyuan.index.infrastructure.utils.MergeUtils;
import com.wlyuan.tools.Page;
import com.wlyuan.tools.PageSort;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @author yuanjie
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class WaybillIndexSearchService extends AbstractSearchService {
    private final WaybillIndexProperties indexProperties;
    private final RestHighLevelClient restHighLevelClient;

    @Override
    public String getIndexName() {
        return WaybillIndex.class.getAnnotation(Document.class).indexName();
    }

    private List<WaybillIndexFilterDTO> applyPre(String name, List<WaybillIndexFilterDTO> filters) {
        var preFilter = indexProperties.getFilters().get(name);
        if (null == preFilter) {
            return filters;
        }

        return filters.stream()
                .map(filter -> MergeUtils.fill(filter, preFilter))
                .collect(Collectors.toList());
    }

    public Page<WaybillIndexVO> search(String name) {
        var filter = this.indexProperties.getFilters().get(name);
        if (null == filter) {
            filter = new WaybillIndexFilterDTO();
        }
        return this.search(filter);
    }

    public Page<WaybillIndexVO> search(WaybillIndexFilterDTO filter) {
        if (logger.isDebugEnabled()) {
            logger.info("运单搜索: {}", JSON.toJSONString(filter));
        }
        var request = createRequest(filter, indexProperties.getRules());
        try {
            var requestOptions = createRequestOptions(filter.getTenantId());
            SearchResponse response = restHighLevelClient.search(request, requestOptions);
            var page = new Page<WaybillIndexVO>(filter.getPage(), filter.getSize());
            page.setRecords(createIndices(response.getHits(), WaybillIndexVO.class));
            page.setTotal(response.getHits().getTotalHits().value);
            return page;
        } catch (Exception e) {
            logger.error("查询运单索引失败: {}", filter, e);
            throw new ValueError("查询运单索引失败");
        }
    }

    public Page<WaybillIndexVO> search(List<WaybillIndexFilterDTO> filters, PageSort page) {
        if (logger.isDebugEnabled()) {
            logger.info("运单搜索: {}", JSON.toJSONString(filters));
        }
        var request = createRequest(filters, indexProperties.getRules(), page);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            var result = new Page<WaybillIndexVO>(page.getPage(), page.getSize());
            result.setRecords(createIndices(response.getHits(), WaybillIndexVO.class));
            result.setTotal(response.getHits().getTotalHits().value);
            return result;
        } catch (Exception e) {
            logger.error("查询运单索引失败: {} {}", filters, page, e);
            throw new ValueError("查询运单索引失败");
        }
    }

    public Page<WaybillIndexVO> search(WaybillIndexSearchDTO search) {
        if (CollectionUtils.isEmpty(search.getFilters())) {
            return this.search(search.getFilterName());
        }

        var filters = this.applyPre(search.getFilterName(), search.getFilters());
        return this.search(filters, search);
    }

    public WaybillIndexScroll scroll(WaybillIndexFilterDTO filter) {
        var request = createScrollRequest(filter, indexProperties.getRules());
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            var indices = createIndices(response.getHits(), WaybillIndex.class);
            var total = response.getHits().getTotalHits().value;
            return new WaybillIndexScroll(response.getScrollId(), total, indices);
        } catch (Exception e) {
            logger.error("查询运单索引失败: {}", filter, e);
            throw new ValueError("查询运单索引失败");
        }
    }

    public WaybillIndexScroll scroll(String scrollId) {
        try {
            SearchScrollRequest request = createScrollRequest(scrollId);
            SearchResponse response = restHighLevelClient.scroll(request, RequestOptions.DEFAULT);
            var indices = createIndices(response.getHits(), WaybillIndex.class);
            var total = response.getHits().getTotalHits().value;
            return new WaybillIndexScroll(response.getScrollId(), total, indices);
        } catch (Exception e) {
            logger.error("查询运单索引失败: {}", scrollId, e);
            throw new ValueError("查询运单索引失败");
        }
    }

    public WaybillIndexSumVO searchSum(WaybillIndexFilterDTO filter) {
        var sourceBuilder = createBuilder(filter, indexProperties.getRules());
        var sumBuilder = new WaybillIndexSumBuilder();
        sumBuilder.build(sourceBuilder);

        if (logger.isDebugEnabled()) {
            logger.debug("运单索引统计: {}", sourceBuilder);
        }

        SearchRequest request = new SearchRequest();
        request.indices(getIndexName());
        request.source(sourceBuilder);
        try {
            SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
            return sumBuilder.collect(response.getAggregations());
        } catch (Exception e) {
            logger.error("运单索引统计失败: {}", sourceBuilder, e);
            throw new ValueError("运单索引统计失败");
        }
    }
}